﻿using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Configuration.Json;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.FileProviders;
using Microsoft.Extensions.Options;
using Microsoft.Extensions.Primitives;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApp2
{
    // B站视频
    // https://www.bilibili.com/video/BV1zX4y1N7SQ?p=7
    public static class Sample01
    {
        //三个接口
        public interface IAccount { }
        public interface IMessage { }
        public interface ITool { }

        //用基类方式基础
        public class Base
        {
            public Base()
            {
                Console.WriteLine($"{GetType().Name}已创建");
            }
        }

        //实现接口的三个类,也就是服务
        public class Account : Base, IAccount { }
        public class Message : Base, IMessage { }
        public class Tool : Base, ITool { }

        public static void Run()
        {
            //ServiceCollection是IOC容器,也就是服务的集合,用于实现服务的组合
            //可专注于当前的实现而不需要管父类的实现.
            //它是作用域的根容器
            var provider = new ServiceCollection()

            //父类获取的方式
            .AddTransient<Base, Account>()//生命周期:瞬间 == 一次性
            .AddScoped<Base, Message>()   //生命周期:范围 == 当前容器
            .AddSingleton<Base, Tool>()   //生命周期:单例 == 根容器
            .BuildServiceProvider();      //建立服务

            //在根容器下创建子容器(没有孙容器)
            var child1 = provider.CreateScope().ServiceProvider;
            var child2 = provider.CreateScope().ServiceProvider;

            GetService<IAccount>(child1);
            GetService<IMessage>(child1);
            GetService<ITool>(child1);
            Console.WriteLine("------------------------------------");
            GetService<IAccount>(child2);
            GetService<IMessage>(child2);
            GetService<ITool>(child2);

            //获取服务的时候类型才创建呢!
            var services = provider.GetServices<Base>().ToList();
            //获取对应的实现类
            //Debug.Assert(services.OfType<Account>().Any());
            //Debug.Assert(services.OfType<Message>().Any());
            //Debug.Assert(services.OfType<Tool>().Any());
        }

        //获取两次
        private static void GetService<T>(IServiceProvider provider)
        {
            provider.GetService<T>();
            provider.GetService<T>();
        }
    }

    public static class SampleDirectory
    {
        public static void Run()
        {
            //IOC容器,读取路径下所有的文件
            var provider = new ServiceCollection()
            .AddSingleton<IFileProvider>(new PhysicalFileProvider(@"D:\桌面"))
            .AddSingleton<FileManager>()
            .BuildServiceProvider();
            var fileManager = provider.GetService<FileManager>();
            fileManager.Dir();
        }
        public class FileManager
        {
            IFileProvider _fileProvider;
            public FileManager(IFileProvider fileProvider)
            {
                _fileProvider = fileProvider;
            }

            public void Dir()
            {
                var indent = -1;
                Get("");

                void Get(string subpath)
                {
                    indent++;
                    foreach (var fileinfo in _fileProvider.GetDirectoryContents(subpath))
                    {
                        Console.WriteLine('\t' + indent + '\t' + fileinfo.Name);
                        if (fileinfo.IsDirectory)
                        {
                            Get($@"{subpath}\{fileinfo.Name}");
                        }
                    }
                }
            }
        }
    }

    public static class SampleFile
    {
        public static void Run()
        {
            //IOC容器,读取路径下所有的文件
            var provider = new ServiceCollection()
#if true
           //外部文件读取
           .AddSingleton<IFileProvider>(new PhysicalFileProvider(@"D:\桌面"))
#else
           //嵌入资源读取
           .AddSingleton<IFileProvider>(new EmbeddedFileProvider(Assembly.GetExecutingAssembly()))//通过当前程序集  
#endif
            .AddSingleton<FileManager>()
            .BuildServiceProvider();
            var fileManager = provider.GetService<FileManager>();
            var con = fileManager.ReadAsync("新建文本文档.txt").Result;
            Console.WriteLine(con);
        }

        public class FileManager
        {
            IFileProvider _fileProvider;
            public FileManager(IFileProvider fileProvider)
            {
                _fileProvider = fileProvider;
            }

            //异步读取文件
            public async Task<string> ReadAsync(string path)
            {
                await using var stream = _fileProvider.GetFileInfo(path).CreateReadStream();
                var buffer = new byte[stream.Length];
                await stream.ReadAsync(buffer, 0, buffer.Length);
                return Encoding.Default.GetString(buffer);
            }
        }
    }

    public static class SampleFile2
    {
        public static void Run()
        {
            //IOC容器,读取路径下所有的文件
            var provider = new ServiceCollection()
#if true
           //外部文件读取
           .AddSingleton<IFileProvider>(new PhysicalFileProvider(@"D:\桌面"))
#else
           //嵌入资源读取
            .AddSingleton<IFileProvider>(new EmbeddedFileProvider(Assembly.GetExecutingAssembly()))//通过当前程序集  
#endif
            .AddSingleton<FileManager>()
            .BuildServiceProvider();
            var fileManager = provider.GetService<FileManager>();
#if false
            //读取文件
            var con = fileManager.ReadAsync("新建文本文档.txt").Result; 
            Console.WriteLine(con);
#endif
#if true
            //监控文件
            fileManager.WatchAsync("新建文本文档.txt").Wait();
            Console.Read();
#endif

        }
        public class FileManager
        {
            IFileProvider _fileProvider;
            public FileManager(IFileProvider fileProvider)
            {
                _fileProvider = fileProvider;
            }

            /// <summary>
            /// 异步读取文件
            /// </summary>
            /// <param name="path"></param>
            /// <returns></returns>
            public async Task<string> ReadAsync(string path)
            {
                await using var stream = _fileProvider.GetFileInfo(path).CreateReadStream();
                var buffer = new byte[stream.Length];
                await stream.ReadAsync(buffer, 0, buffer.Length);
                return Encoding.Default.GetString(buffer);
            }

            /// <summary>
            /// 监控文件
            /// </summary>
            /// <param name="path">可以是个*.*</param>
            /// <returns></returns>
            public async Task WatchAsync(string path)
            {
                Console.WriteLine(await ReadAsync(path));
                //接收改变通知
                //文件监控是通过Watch(可以是个*.*)的IChangeToken接口实现
                ChangeToken.OnChange(() =>
                        _fileProvider.Watch(path),//提供端口给它接收文件改变的通知,
                        async () =>
                        {
                            //收到通知的时候就调用后面这个委托
                            Console.Clear();
                            Console.WriteLine(await ReadAsync(path));
                        });
            }
        }
    }

    //配置 
    public static class SampleConfigDemo
    {
        public class AppConfigDemo
        {
            public string Name { get; set; }
            public string StartDate { get; set; }
            public string EndDate { get; set; }
            public Behavior Behavior { get; set; }
        }
        public class Behavior
        {
            public bool IsRead { get; set; }
            public bool IsWrite { get; set; }
        }

        public static void Run()
        {
            //var source = new Dictionary<string, string>
            //{
            //    ["Name"] = "AppConfig",
            //    ["StartDate"] = "2020年5月1日",
            //    ["EndDate"] = "2020年5月5日",
            //};

            //ConfigurationBuilder    属于nuget包: Microsoft.Extensions.Configuration    
            //JsonConfigurationSource 属于nuget包: Microsoft.Extensions.Configuration.Json
            //config.Get<>            属于nuget包: Microsoft.Extensions.Configuration.Binder
            //创建配置对象,add配置源对象是内存源
            var config = new ConfigurationBuilder()
               .AddJsonFile("新建文本文档.json", true, true)//通过第三个参数实现监控功能
               .Build();

            //第一次读取配置
            Read(config.Get<AppConfigDemo>());

            //配置一旦更改就读取
            ChangeToken.OnChange(() =>
                    config.GetReloadToken(),//提供端口给它接收文件改变的通知
                    () =>
                    {
                        //收到通知的时候就调用后面这个委托 
                        Read(config.Get<AppConfigDemo>());
                    });
            Console.Read();//一定要写,否则程序就结束了..
        }

        private static void Read(AppConfigDemo options)
        {
            Console.Clear();
            Console.WriteLine($"Name:{options.Name}");
            Console.WriteLine($"StartDate:{options.StartDate}");
            Console.WriteLine($"EndDate:{options.EndDate}");
            Console.WriteLine($"Behavior.IsRead: {options.Behavior.IsRead}");
            Console.WriteLine($"Behavior.Iswrite:{options.Behavior.IsWrite}");
        }
    }

    public static class 系统变量
    {
        public class AppConfigDemo
        {
            public string Name { get; set; }
            public string Version { get; set; }
        }
        public static void Run()
        {
            //设置环境变量
            Environment.SetEnvironmentVariable("APP_NAME", "AppDemo");
            Environment.SetEnvironmentVariable("APP_VER", "Alpha");
            //配置生成
            //AddEnvironmentVariables  属于nuget包: Microsoft.Extensions.Configuration.Environmentvariables
            var config = new ConfigurationBuilder()
            .AddEnvironmentVariables("APP_")
            .Build()
            .Get<AppConfigDemo>();
            Console.WriteLine($"Name:{config.Name}");
            Console.WriteLine($"Ver:{config.Version}");
        }
    }

    public static class 命令行
    {
        public class AppConfigDemo
        {
            public string Name { get; set; }
            public string Version { get; set; }
        }
        public static void Run(string[] args)
        {
            //-号会自动映射到上面数据 用powershell在项目文件夹(不是bin): dotnet run -n MyApp -ver alpha
            var mapping = new Dictionary<string, string>()
            {
                ["-n"] = "Name",
                ["-ver"] = "Version"
            };
            //AddCommandLine 属于nuget包: Microsoft.Extensions.Configuration.AddCommandLine
            var config = new ConfigurationBuilder()
            .AddCommandLine(args, mapping)
            .Build()
            .Get<AppConfigDemo>();
            Console.WriteLine($"Name:{config.Name}");
            Console.WriteLine($"Version:{config.Version}");
            Console.WriteLine($"Version:{config.Version}");
            Console.WriteLine($"Version:{config.Version}");
            Console.WriteLine($"Version:{config.Version}");

        }
    }

    //xml读取
    public static class XmlConfigDemo
    {
        public class AppConfigDemo
        {
            public string Name { get; set; }
            public string StartDate { get; set; }
            public string EndDate { get; set; }
            public Behavior Behavior { get; set; }
        }
        public class Behavior
        {
            public bool IsRead { get; set; }
            public bool IsWrite { get; set; }
        }
        public static void Run()
        {
            //AddXmlFile 属于nuget包: Microsoft.Extensions.Configuration.XmlFile
            var options = new ConfigurationBuilder()
            .AddXmlFile("App.config")
            .Build()
            .GetSection(nameof(AppConfigDemo))//它和json不同的是必须要根对象configuration
            .Get<AppConfigDemo>();

            Console.WriteLine($"Name:{options.Name} ");
            Console.WriteLine($"StartDate:{options.StartDate}");
            Console.WriteLine($"EndDate:ioptions.EndDate]");
            Console.WriteLine($"Behavior.IsRead:{options.Behavior.IsRead}");
            Console.WriteLine($"Behavior.IsWrite:{options.Behavior.IsWrite}");

        }

    }

    public static class Sample08
    {
        public static void Run()
        {
            var source = new Dictionary<string, string>
            {
                ["A:B:C"] = "ABC"
            };
            var root = new ConfigurationBuilder()
              .AddInMemoryCollection(source)
              .Build();

            //了解数据结构的树形结构
            //这里就算讲 AddXmlFile AddCommandLine 这些是怎么制作成拓展的.
            //以及你要怎么做一个你需要的拓展

            // 总结:
            // IConfigurationBuilder 对象利用注册在它上面的，
            // 所有 IConfigurationSource 对象提供的 IConfigurationProvider 对象
            // 来读取原始配置数据，并创建出相应的 IConfiguration 对象

            var section1 = root.GetSection(key: "A:B:C");
            var section2 = root.GetSection(key: "A:B").GetSection(key: "C");
            var section3 = root.GetSection(key: "A").GetSection(key: "B:C");

            Debug.Assert(section1.Value == "ABC");
            Debug.Assert(section2.Value == "ABC");
            Debug.Assert(section3.Value == "ABC");

            Debug.Assert(!ReferenceEquals(section1, section2));
            Debug.Assert(!ReferenceEquals(section1, section3));
            Debug.Assert(root.GetSection("D") != null);
        }
    }

    //了解一下配置模式,读取单套配置
    public static class Sample01_01
    {
        public class AppConfigDemo
        {
            public string Name { get; set; }
            public string StartDate { get; set; }
            public string EndDate { get; set; }
            public Behavior Behavior { get; set; }
        }
        public class Behavior
        {
            public bool IsRead { get; set; }
            public bool IsWrite { get; set; }
        }

        public static void Run()
        {
            //ConfigurationBuilder    属于nuget包: Microsoft.Extensions.Configuration    
            //JsonConfigurationSource 属于nuget包: Microsoft.Extensions.Configuration.Json
            //config.Get<>            属于nuget包: Microsoft.Extensions.Configuration.Binder
            //AddOptions 属于nuget包:  Microsoft.Extensions.Options

            //创建配置对象,add配置源对象是内存源
            var configuration = new ConfigurationBuilder()
               .AddJsonFile("appsettings.json", true, true)//通过第三个参数实现监控功能
               .Build();

            //Options模式必须要依赖注入
            //配置对象和AppConfigDemo选项对象做映射,绑定到IOptions上
            var serviceProvider = new ServiceCollection()
            .AddOptions()
            .Configure<AppConfigDemo>(configuration)
            .BuildServiceProvider();

            //这里再获取IOptions上面的对象
            var options = serviceProvider.GetRequiredService<IOptions<AppConfigDemo>>();
            var appConfig = options.Value;

            Console.WriteLine($"Name:{appConfig.Name}");
            Console.WriteLine($"StartDate: {appConfig.StartDate}");
            Console.WriteLine($"EndDate:{appConfig.EndDate}");
            Console.WriteLine($"Behavior.IsRead : {appConfig.Behavior.IsRead}");
            Console.WriteLine($"Behavior.IsWrite: {appConfig.Behavior.IsWrite}");

        }
    }

    //了解一下配置模式,读取单套配置,数据联动
    public static class Sample01_01_02
    {
        public class AppConfigDemo
        {
            public string Name { get; set; }
            public string StartDate { get; set; }
            public string EndDate { get; set; }
            public Behavior Behavior { get; set; }
        }
        public class Behavior
        {
            public bool IsRead { get; set; }
            public bool IsWrite { get; set; }
        }

        public static void Run()
        {
            //ConfigurationBuilder    属于nuget包: Microsoft.Extensions.Configuration    
            //JsonConfigurationSource 属于nuget包: Microsoft.Extensions.Configuration.Json
            //config.Get<>            属于nuget包: Microsoft.Extensions.Configuration.Binder
            //AddOptions 属于nuget包:  Microsoft.Extensions.Options

            //创建配置对象,add配置源对象是内存源
            var configuration = new ConfigurationBuilder()
               .AddJsonFile("appsettings.json", true, true)//通过第三个参数实现监控功能
               .Build();

            //Options模式必须要依赖注入
            //配置对象和AppConfigDemo选项对象做映射,绑定到IOptions上
            var serviceProvider = new ServiceCollection()
            .AddOptions()
            .Configure<AppConfigDemo>(configuration)
            .BuildServiceProvider();

            //这里的对象实现联动**IOptionsMonitor**带监控的选项
            var options = serviceProvider.GetRequiredService<IOptionsMonitor<AppConfigDemo>>();

            //修改了执行显示
            options.OnChange(appConfig =>
            {
                Console.Clear();
                Console.WriteLine($"Name:{appConfig.Name}");
                Console.WriteLine($"StartDate: {appConfig.StartDate}");
                Console.WriteLine($"EndDate:{appConfig.EndDate}");
                Console.WriteLine($"Behavior.IsRead : {appConfig.Behavior.IsRead}");
                Console.WriteLine($"Behavior.IsWrite: {appConfig.Behavior.IsWrite}");
            });

            //第一次执行显示
            var appConfig = options.CurrentValue;
            Console.WriteLine($"Name:{appConfig.Name}");
            Console.WriteLine($"StartDate: {appConfig.StartDate}");
            Console.WriteLine($"EndDate:{appConfig.EndDate}");
            Console.WriteLine($"Behavior.IsRead : {appConfig.Behavior.IsRead}");
            Console.WriteLine($"Behavior.IsWrite: {appConfig.Behavior.IsWrite}");

            Console.Read();
        }
    }


    //读取json内的多套配置
    public static class Sample01_02
    {
        public class AppConfigDemo
        {
            public string Name { get; set; }
            public string StartDate { get; set; }
            public string EndDate { get; set; }
        }

        public static void Run()
        {
            //ConfigurationBuilder    属于nuget包: Microsoft.Extensions.Configuration    
            //JsonConfigurationSource 属于nuget包: Microsoft.Extensions.Configuration.Json
            //config.Get<>            属于nuget包: Microsoft.Extensions.Configuration.Binder
            //AddOptions 属于nuget包:  Microsoft.Extensions.Options

            //创建配置对象,add配置源对象是内存源
            var configuration = new ConfigurationBuilder()
               .AddJsonFile("muti.json", true, true)//通过第三个参数实现监控功能
               .Build();

            //Options模式必须要依赖注入
            //配置对象和AppConfigDemo选项对象做映射,绑定到IOptions上

            string defaultAppStr = "DefaultApp";
            string customAppStr = "CustomApp";

            var serviceProvider = new ServiceCollection()
            .AddOptions()
            .Configure<AppConfigDemo>(defaultAppStr, configuration.GetSection(defaultAppStr))
            .Configure<AppConfigDemo>(customAppStr, configuration.GetSection(customAppStr))
            .BuildServiceProvider();

            //这里再获取IOptionsSnapshot上面的对象
            var options = serviceProvider.GetRequiredService<IOptionsSnapshot<AppConfigDemo>>();
            var defaultApp = options.Get(defaultAppStr);
            var customApp = options.Get(customAppStr);

            Console.WriteLine($"Name:{defaultApp.Name}");
            Console.WriteLine($"StartDate: {defaultApp.StartDate}");
            Console.WriteLine($"EndDate:{defaultApp.EndDate}");

            Console.WriteLine($"Name:{customApp.Name}");
            Console.WriteLine($"StartDate: {customApp.StartDate}");
            Console.WriteLine($"EndDate:{customApp.EndDate}");

        }
    }



    //读取json内的多套配置--修改了然后更新
    public static class Sample01_02_02
    {
        public class AppConfigDemo
        {
            public string Name { get; set; }
            public string StartDate { get; set; }
            public string EndDate { get; set; }
            public Behavior Behavior { get; set; }
        }
        public class Behavior
        {
            public bool IsRead { get; set; }
            public bool IsWrite { get; set; }
        }

        public static void Run()
        {
            //ConfigurationBuilder    属于nuget包: Microsoft.Extensions.Configuration    
            //JsonConfigurationSource 属于nuget包: Microsoft.Extensions.Configuration.Json
            //config.Get<>            属于nuget包: Microsoft.Extensions.Configuration.Binder
            //AddOptions 属于nuget包:  Microsoft.Extensions.Options

            //创建配置对象,add配置源对象是内存源
            var configuration = new ConfigurationBuilder()
               .AddJsonFile("muti.json", true, true)//通过第三个参数实现监控功能
               .Build();

            //Options模式必须要依赖注入
            //配置对象和AppConfigDemo选项对象做映射,绑定到IOptions上

            string defaultAppStr = "DefaultApp";
            string customAppStr = "CustomApp";

            var serviceProvider = new ServiceCollection()
            .AddOptions()
            .Configure<AppConfigDemo>(defaultAppStr, configuration.GetSection(defaultAppStr))
            .Configure<AppConfigDemo>(customAppStr, configuration.GetSection(customAppStr))
            .BuildServiceProvider();

            //这里的对象实现联动**IOptionsMonitor**带监控的选项
            var options = serviceProvider.GetRequiredService<IOptionsMonitor<AppConfigDemo>>();

            //修改了执行显示 
            options.OnChange((appConfig, configName) =>
            {
                //多套配置根据名称处理回调,根据名称来得到配置节点
                //这里很奇怪,回调的时候每次保存就切换一次分支.
                Console.Clear();
                if (configName == defaultAppStr)
                {
                    Console.WriteLine("****************************" + configName);
                }
                if (configName == customAppStr)
                {
                    Console.WriteLine("+++++++++++++++++++++++++++++" + configName);
                }
                Console.WriteLine($"Name:{appConfig.Name}");
                Console.WriteLine($"StartDate: {appConfig.StartDate}");
                Console.WriteLine($"EndDate:{appConfig.EndDate}");
            });

            //第一次显示
            var defaultApp = options.Get(defaultAppStr);
            var customApp = options.Get(customAppStr);

            Console.WriteLine($"Name:{defaultApp.Name}");
            Console.WriteLine($"StartDate: {defaultApp.StartDate}");
            Console.WriteLine($"EndDate:{defaultApp.EndDate}");

            Console.WriteLine($"Name:{customApp.Name}");
            Console.WriteLine($"StartDate: {customApp.StartDate}");
            Console.WriteLine($"EndDate:{customApp.EndDate}");
            Console.Read();

        }
    }


    //配置委托
    public static class Sample01_02_03
    {
        public class AppConfigDemo
        {
            public string Name { get; set; }
            public string StartDate { get; set; }
            public string EndDate { get; set; }
        }

        public static void Run()
        {
            var serviceProvider = new ServiceCollection()
            .AddOptions()
            .Configure<AppConfigDemo>(config =>
            {
                config.Name = "Dddddddd";
                config.StartDate = "123";
                config.EndDate = "456";
            })
            .BuildServiceProvider();

            //这里的对象实现联动**IOptionsMonitor**带监控的选项
            var options = serviceProvider.GetRequiredService<IOptions<AppConfigDemo>>();

            var defaultApp = options.Value;

            Console.WriteLine($"Name:{defaultApp.Name}");
            Console.WriteLine($"StartDate: {defaultApp.StartDate}");
            Console.WriteLine($"EndDate:{defaultApp.EndDate}");
            Console.Read();

        }
    }

    //验证配置有效性--命令行配置源 
    public static class 命令行验证配置有效性
    {
        public class AppConfigDemo
        {
            public string Name { get; set; }
            public string Version { get; set; }
        }
        public static void Run(string[] args)
        {
            //-号会自动映射到上面数据 用powershell在项目文件夹(不是bin): dotnet run -n MyApp -ver alpha
            var mapping = new Dictionary<string, string>()
            {
                ["-n"] = "Name",
                ["-ver"] = "Version"
            };
            //AddCommandLine 属于nuget包: Microsoft.Extensions.Configuration.AddCommandLine
            var config = new ConfigurationBuilder()
            .AddCommandLine(args, mapping)
            .Build();

            var services = new ServiceCollection();
            services.AddOptions<AppConfigDemo>()
             .Configure(options =>
             {
                 //初始化配置
                 options.Name = config["Name"] ?? "";
                 options.Version = config["Version"] ?? "";
             })
            //验证,委托:Func返回,没有返回报告异常
            .Validate(demo => demo.Name.EndsWith("App"), "Name 参数无效")
            .Validate(demo => demo.Version.StartsWith("Alpha"), "Version参数无效");

            try
            {
                var options = services.BuildServiceProvider()
                    .GetRequiredService<IOptions<AppConfigDemo>>().Value;
                Console.WriteLine($"Name:{options.Name}");
                Console.WriteLine($"Version:{options.Version}");
            }
            catch (OptionsValidationException ex)
            {
                Console.WriteLine("选项验证异常:" + ex.Message);
            }
        }
    }





    class Program
    {
        static void Main(string[] args)
        {
            //关联文件的修改
            //Sample01.Run();
            //Console.Read();
            //SampleDirectory.Run();
            //SampleFile.Run();
            //SampleFile2.Run();

            //配置
            //SampleConfigDemo.Run();
            //系统变量.Run();
            //命令行.Run(args); 
            //XmlConfigDemo.Run();

            //Sample08.Run();

            //Sample01_01.Run();
            //Sample01_01_02.Run();

            //Sample01_02.Run();
            // Sample01_02_02.Run();
            //Sample01_02_03.Run();

            //项目目录(不是bin),路径栏输入powershell,在里面输入: dotnet run -n asd -ver ddddf
            //然后提示 参数无效
            //然后输入: dotnet run -n App -ver Alpha
            //都有效了
            命令行验证配置有效性.Run(args);

            Console.WriteLine($"任意键结束");
            Console.Read();//一定要写,否则程序就结束了..
        }
    }
}
